#pragma once
#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <vector>
#include <sys/types.h>
#include <sys/socket.h>

// 这里的协议是用于服务网络版计算器。

// 解决单个报文数据读取问题：使用空格作为分隔符，定义成宏方便根据需求修改
#define SPACE " "
#define SPACE_LINE strlen(SPACE)
// 解决粘包问题：使用特殊字符（#）区分各报文
#define SEP "#"
#define SEP_LINE strlen(SEP)

/// 请求：结构体对象 ///
class Request
{
public:
    // 构造
    Request() {};
    Request(int x, int y, char op)
        : x_(x), y_(y), op_(op)
    {
    }

    // 对请求进行序列化（结构化数据→字节流数据）
    std::string Serialize() // 将x_、y_、op_
    {
        // version1： "x_[空格] op_[空格] y_"
        std::string str;
        str = std::to_string(x_); // 先将对应的运算数转换为字符类型：例如32-->"32"。这里注意与ASCII中值为32的字符区别
        str += SPACE;             // 中间以我们设置的间隔符分割（为了反序列化时能够提取每部分）
        str += op_;               // op_本身就是char类型
        str += SPACE;
        str += std::to_string(y_);
        return str;
    }

    // 对请求进行反序列化（字节流数据→结构化数据）
    bool Deserialized(const std::string &str) // 获取x_、y_、op_
    {
        //----------------------------------
        // version1： "x_[空格] op_[空格] y_" 根据分隔符提取有效数放入结构化对象中
        // 例如："1234[空格]+[空格]5678"
        // a、找左运算数
        std::size_t left_oper = str.find(SPACE);
        if (left_oper == std::string::npos) // 没找到
            return false;
        // b、找右运算数
        std::size_t right_oper = str.rfind(SPACE);
        if (right_oper == std::string::npos) // 没找到
            return false;
        // c、提取运算数，赋值给结构化对象成员
        x_ = atoi((str.substr(0, left_oper)).c_str());            // string substr (size_t pos = 0, size_t len = npos) const;
        y_ = atoi((str.substr(right_oper + SPACE_LINE).c_str())); // 注意这里右运算符需要将[空格]跳过
        if (left_oper + SPACE_LINE > str.size())
            return false;
        else
            op_ = str[left_oper + SPACE_LINE]; // 提取运算符时也要注意跳过分隔符[空格]
        return true;
        //----------------------------------
    }

public:
    int x_;   // 左运算数
    int y_;   // 右运算数
    char op_; // 运算符
};

/// 响应：结构体对象 ///
class Response
{
public:
    // 构造函数
    Response(int result, int code)
        : result_(result), code_(code)
    {
    }
    Response() {}

    // 析构函数
    ~Response() {}

    // 对响应序列化（结构化数据→字节流数据）
    std::string Serialize()
    {
        // version1："code_ [空格] result_"
        // 例如："0[空格]6912"
        std::string str;
        str = std::to_string(code_);
        str += SPACE;
        str += std::to_string(result_);
        return str;
    }

    // 对响应反序列化（字节流数据→结构化数据）
    bool Deserialized(const std::string &str)
    {
        //----------------------------------
        // version1："code_ [空格] result_"
        // 例如："0[空格]6912"
        // a、找分隔符
        std::size_t pos = str.find(SPACE);
        if (pos == std::string::npos) // 没找到
            return false;
        // b、获取状态码
        code_ = atoi((str.substr(0, pos)).c_str());
        // c、获取计算结果
        result_ = atoi((str.substr(pos + SPACE_LINE)).c_str());
        return true;
        //----------------------------------
    }

public:
    int result_; // 计算结果
    int code_;   // 状态码：用于判断结果是否正常
};




// 我们要把传入进来的缓冲区进行切分
// 1. 从buffer中被切走的部分，也同时要从buffer中移除
// 2. 可能会存在多个报文，多个报文要依次放入out
// buffer: 输入输出型参数
// out: 输出型参数
void Decode(std::string &buffer, std::vector<std::string> *out)
{
    // 100 +
    // 100 + 123#110
    // 100 + 123#110 / 2
    while (true)
    {
        std::size_t pos = buffer.find(SEP);// 分包：找SEP
        if (pos == std::string::npos) // 没找到，说明本次报文不完整，需要继续读取/接收
            break;

        // 执行到此，说明确实有#，但不一定代表数据完整。
        std::string message = buffer.substr(0, pos);//找单个子串
        buffer.erase(0, pos + SEP_LINE);//移除buffer中相关子串
        out->push_back(message);

        // std::cout << "debug:  message is: " << message << " , then the buffer is:  " << buffer << std::endl;
        // sleep(1); // 用于测试
    }
}

// 构建应答报文：
std::string Encode(std::string &str)
{
    // 1、加上SEP分隔符
    str += SEP;
    return str;
}
